package main

import (
	"encoding/hex"
	"fmt"
	"math/big"
	"strings"

	"gitee.com/caivega/easm/compile"
	"gitee.com/caivega/easm/core"
	"gitee.com/caivega/easm/evm"
	"gitee.com/caivega/easm/util"
	"github.com/ethereum/go-ethereum/core/vm"
)

func GetDeployCode(code []byte) []byte {
	cb := core.CreateBuffer()

	op := byte(vm.PUSH2)
	offset := byte(0x0a)
	n := new(big.Int).SetUint64(uint64(len(code)))
	data := n.Bytes()
	cl := len(data)
	switch {
	case cl == 0:
		panic("empty code")
	case cl == 1:
		op--
		offset--
	case cl > 2:
		panic("too large code")
	}

	cb.Append([]byte{
		op,
	})
	cb.Append(data)
	cb.Append([]byte{
		byte(vm.DUP1),
	})
	cb.Append([]byte{
		byte(vm.PUSH1), offset,
		byte(vm.PUSH0),
		byte(vm.CODECOPY),
	})
	cb.Append([]byte{
		byte(vm.PUSH0),
		byte(vm.RETURN),
	})
	cb.Append(code)

	return cb.Bytes()
}

func main() {
	testFiles := []string{
		"./test/000_hello.asm",
		"./test/001_interface.asm",
		"./test/002_storage.asm",
		"./test/003_memory.asm",
		"./test/004_if.asm",
		"./test/005_label.asm",
		"./test/006_for.asm",
	}
	for index, testFile := range testFiles {
		fmt.Println("-------------------------")
		fmt.Println(index, testFile)
		current := compile.Create(compile.CreateBlock(), core.CreateConfig())
		if index == (len(testFiles) - 1) {
			current.SetDebug(true)
		}
		compile.LoadModule(current, testFile)
		if current.IsDebug() {
			compile.Dump(current)
		}
		code := compile.Generate(current)
		if current.IsDebug() {
			deployCode := GetDeployCode(code)
			// evm.Run(deployCode, nil, code)
			fmt.Println("deploy:", len(deployCode), hex.EncodeToString(deployCode))
		}
		inputs := make([][]byte, 0)
		shouldReturneds := make([][]byte, 0)
		tests, ok := current.GetComments(core.ASSERT_TEST)
		if ok {
			for i := 0; i < len(tests); i++ {
				items := strings.Split(tests[i], ",")
				if len(items) != 2 {
					panic("error test")
				}
				input, err := util.HexToBytes(items[0])
				if err != nil {
					panic(err)
				}
				inputs = append(inputs, input)
				if len(items[1]) > 0 {
					shouldReturned, err := util.HexToBytes(items[1])
					if err != nil {
						panic(err)
					}
					shouldReturneds = append(shouldReturneds, shouldReturned)
				} else {
					shouldReturneds = append(shouldReturneds, nil)
				}
			}
		} else {
			input, _ := current.GetComment(core.ASSERT_INPUT)
			shouldReturned, _ := current.GetComment(core.ASSERT_RETURN)
			inputs = append(inputs, input)
			shouldReturneds = append(shouldReturneds, shouldReturned)
		}
		evm.Run(code, inputs, shouldReturneds)
	}
}
